import { Component, Host, Optional } from '@angular/core';

import { HeroCacheService } from './hero-cache.service';
import { LoggerService }    from './logger.service';

@Component({
  selector: 'hero-contact',
  template: `
  <div>Phone #: {{phoneNumber}}
  <span *ngIf="hasLogger">!!!</span></div>`
})

/**
 * 使用 @Optional 和 @Host 装饰器来限定依赖查找方式
 * 当组件申请一个依赖时， Angular 从该组件本身的注入器开始，沿着依赖注入器的树往上找，
 * 直到找到第一个符合要求的提供商。如果 Angular 不能在这个过程中找到合适的依赖，它就会抛出一个错误。

 * 大部分时候，我们确实 想要 这个行为。 但是有时候，需要限制这个 ( 依赖 ) 查找逻辑，
 * 且 / 或提供一个缺失的依赖。 单独或联合使用 @Host 和 @Optional 限定型装饰器，就可以修改 
 * Angular 的查找行为。

 * 当 Angular 找不到依赖时， @Optional 装饰器会告诉 Angular 继续执行。 Angular 
 * 把此注入参数设置为 null( 而不用默认的抛出错误的行为 ) 。
 */


export class HeroContactComponent {

  hasLogger = false;

  /**  注意看构造函数的参数
   * @Host() 函数是 heroCache 属性的装饰器，确保从其父组件 HeroBioComponent 
   * 得到一个缓存服务。如果该父组件不存在这个服务， Angular 就会抛出错误，即使组
   * 件树里的再上级有某个组件拥有这个服务， Angular 也会抛出错误。

   * 另一个 @Host() 函数是属性 loggerService 的装饰器，我们知道在应用程序中，
   * 只有一个 LoggerService 实例，也就是在 AppComponent 级提供的服务。 该宿主
   *  HeroBioComponent 没有自己的 LoggerService 提供商。

   * 如果没有同时使用 @Optional() 装饰器的话， Angular 就会抛出错误。多亏了 @Optional() ，
   * Angular 把 loggerService 设置为 null ，并继续执行组件而不会抛出错误。
   */

  constructor(
      // @Host 装饰器将把往上搜索的行为截止在 宿主组件
      @Host() // limit to the host component's instance of the HeroCacheService
      private heroCache: HeroCacheService,
      
      // 宿主组件通常是申请这个依赖的组件。但当这个组件被投影 (projected) 
      // 进一个 父组件 后，这个父组件就变成了宿主。
      @Host()     // limit search for logger; hides the application-wide logger
      @Optional() // ok if the logger doesn't exist
      private loggerService: LoggerService
  ) {
    if (loggerService) {
      this.hasLogger = true;
      loggerService.logInfo('HeroContactComponent can log!');
    }
  }

  get phoneNumber() { return this.heroCache.hero.phone; }

}
